<template>
	<view class="xt-date-picker-main">
		<xtTransition :customStyle='style' :show='mask && pickerShow' @xtClick="maskClick" :duration="350">
		</xtTransition>
		<xtTransition :show='pickerShow' :duration="300" :customStyle='popupStyle' name='slide-up' :safeAreaTop='true'
			:safeAreaBottom='true'>
			<view class="xt-date-picker">
				<view class="xt-picker-control" v-if="showControl">
					<view class="xt-picker-cancel" :style="{
					color:cancelColor
				}" @click="close">{{cancelText}}</view>
					<view class="xt-picker-title">{{title}}</view>
					<view class="xt-picker-confirm" :style="{
					color:confirmColor
				}" @click="confirm">{{confirmText}}</view>
				</view>
				<picker-view class="xt-picker-view" :style="{
					height:`${handleHeight(itemHeight,'px',visibleItemCount)}`,
				}" mask-style='background: transparent' :indicatorStyle="`height:${handleHeight(itemHeight)}`"
					:immediate-change='immediateChange' @change='pickerChange' :value="pickerIndex">
					<picker-view-column v-for="(item,index) in pickerList" :key="index">
						<view :style="{
							height:handleHeight(itemHeight),
							fontWeight: idx === pickerIndex[index] ? 'bold' : 'normal',
							color: idx === pickerIndex[index] ? '#3E3F41' : '#BDBEBE'
						}" class="xt-picker-item" v-for="(i,idx) in pickerList[index]" :key="idx">{{i}}</view>
					</picker-view-column>
				</picker-view>
			</view>
		</xtTransition>
	</view>
</template>
<script>
	import props from "./props.js";
	import xtTransition from '../transition/index.vue';
	/**
	 * DatePicker 日期时间选择器
	 * @description 本组件提供一个日期时间选择器的功能，让开发者开发起来更加得心应手。减少重复的模板代码
	 * @tutorial https://ext.dcloud.net.cn/plugin?name=xt-date-picker
	 *
	 * @property {String} 			value 				绑定值
	 * @property {Boolean} 			show 				是否显示
	 * @property {String} 			mode 				时间类型(默认 date),可选 date|time|datetime|year-month|year|month
	 * @property {String} 			second 				是否显示秒(默认 true)
	 * @property {Number} 			maxDate 			最大时间戳,单位毫秒(限制年月日,默认前后十五年)
	 * @property {Number} 			minDate 			最小时间戳,单位毫秒(限制年月日,默认前后十五年)
	 * @property {String} 			title 				标题
	 * @property {String} 			cancelText 			取消按钮文字
	 * @property {String} 			cancelColor 		取消按钮文字颜色
	 * @property {String} 			confirmText 		确定按钮文字
	 * @property {String} 			confirmColor 		确定按钮文字颜色
	 * @property {Boolean} 			showControl 		是否显示顶部控制栏,包含标题(默认 true)
	 * @property {String} 			bgColor 			弹窗背景颜色(默认 #fff)
	 * @property {Boolean} 			mask 				是否显示遮罩层(默认 true)
	 * @property {Boolean} 			closeOnClickOverlay 点击遮罩层关闭(默认 false)
	 * @property {String | Number}	itemHeight			各列中，单个选项的高度(默认 44)
	 * @property {String | Number}	visibleItemCount	每列中可见选项的数量(默认 5)
	 * @property {Boolean}			immediateChange		是否在手指松开时立即触发change事件(默认 false)
	 * @event {Function}            close            	关闭事件
	 * @event {Function}            confirm            	确定事件
	 * @event {Function} 			change				当选择值变化时触发
	 * @example <xt-date-picker></xt-date-picker>
	 */
	export default {
		name: "xt-date-picker",
		mixins: [props],
		components: {
			xtTransition
		},
		data() {
			return {
				pickerShow: false,
				pickerIndex: [],
				pickerList: [],
				pickerType: [],
				pickerRes: [],
				style: {
					position: 'fixed',
					top: 0,
					left: 0,
					right: 0,
					zIndex: 998,
					bottom: 0,
					'background-color': 'rgba(0,0,0,0.6)',
				},
				popupStyle: {
					position: 'fixed',
					'background-color': '#fff',
					'z-index': 999,
					bottom: '0',
					left: '0',
					right: '0',
					borderRadius: '15rpx 15rpx 0 0'
				},
			};
		},
		watch: {
			show: {
				immediate: true,
				handler(newVal, oldVal) {
					this.pickerShow = newVal;
				}
			},
			// #ifdef VUE3
			modelValue: {
				immediate: true,
				handler(newVal, oldVal) {
					this.pickerRes = newVal || [];
				}
			},
			// #endif
			value: {
				immediate: true,
				handler(newVal, oldVal) {
					this.pickerRes = newVal || [];
				}
			},
		},
		mounted() {
			this.popupStyle["background-color"] = this.bgColor;
			this.init();
		},
		methods: {
			handleHeight(value = 'auto', unit = 'px', count) {
				const regular = /^[\+-]?(\d+\.?\d*|\.\d+|\d\.\d+e\+\d+)$/;
				const unitRegex = /^(\d+)([a-zA-Z]+)$/;
				if (typeof value === 'string' && unitRegex.test(value)) {
					// 提取数字和单位部分
					const match = value.match(unitRegex);
					const numberValue = match[1]; // 数字部分
					const existingUnit = match[2]; // 单位部分
					// 如果数字部分是有效的，则返回数字 + 单位
					if (regular.test(numberValue) && count) {
						return `${parseInt(numberValue) * count}${existingUnit}`;
					} else {
						return `${numberValue}${existingUnit}`;
					}
				} else {
					if (count) {
						return `${parseInt(value) * count}${unit}`;
					} else {
						return `${value}${unit}`;
					}
				}
				return value
			},
			maskClick() {
				if (!this.closeOnClickOverlay) return;
				this.pickerShow = false;
				this.$emit('update:show', false);
				this.$emit('close');
			},
			// arr1 变化前的数组 arr2 变化后的数组
			findFirstChange(arr1, arr2) {
				// 确定比较的最大长度
				let maxLength = Math.max(arr1.length, arr2.length);
				// 遍历数组，找到第一个变化的值
				for (let i = 0; i < maxLength; i++) {
					if (arr1[i] !== arr2[i]) {
						return {
							// 变化的列数，从 0 开始
							index: i,
							// 变化前的值
							oldValue: arr1[i],
							// 变化后的值
							newValue: arr2[i]
						};
					}
				}
				return null;
			},
			init() {
				switch (this.mode) {
					case 'datetime':
						this.getYearData();
						this.getMonthData();
						this.getDayData();
						this.getCurrentHour();
						this.getCurrentMinute();
						this.pickerType = ['year', 'month', 'day', 'hour', 'minute'];
						if (this.second) {
							this.getCurrentSecond();
							this.pickerType.push('second');
						}
						break;
					case 'year':
						this.getYearData();
						this.pickerType = ['year'];
						break;
					case 'month':
						this.getMonthData();
						this.pickerType = ['month'];
						break;
					case 'year-month':
						this.getYearData();
						this.getMonthData();
						this.pickerType = ['year', 'month'];
						break;
					case 'time':
						this.getCurrentHour();
						this.getCurrentMinute();
						this.pickerType = ['hour', 'minute'];
						if (this.second) {
							this.getCurrentSecond();
							this.pickerType.push('second');
						}
						break;
					default:
						this.getYearData();
						this.getMonthData();
						this.getDayData();
						this.pickerType = ['year', 'month', 'day'];
						break;
				}
			},
			pickerChange(e) {
				const vary = this.findFirstChange(this.pickerIndex, e.detail.value);
				this.handleResult(vary);
			},
			handleResult(vary) {
				const index = vary.index;
				const value = vary.newValue;
				const type = this.pickerType[index];
				this.$set(this.pickerIndex, index, value);
				let isCount = false;
				switch (this.mode) {
					case 'datetime':
						isCount = true;
						break;
					case 'date':
						isCount = true;
						break;
					case 'year-month':
						isCount = true;
						break;
					default:
						break;
				};
				if (isCount) {
					const dayIdx = this.pickerType.indexOf('day');
					const monthIdx = this.pickerType.indexOf('month');
					if (this.mode != 'year-month' && type == 'month') {
						this.getDayData(this.pickerList[0][this.pickerIndex[0]], this.pickerList[monthIdx][this
							.pickerIndex[
								monthIdx]
						], this.pickerList[dayIdx][this
							.pickerIndex[dayIdx]
						], dayIdx);
					}
					if (type == 'year' && (this.maxDate || this.minDate)) {
						this.getMonthData(this.pickerList[0][this.pickerIndex[0]], this.pickerList[monthIdx][this
							.pickerIndex[monthIdx]
						], monthIdx);
					}
					if (this.mode != 'year-month' && type != 'day') {
						this.getDayData(this.pickerList[0][this.pickerIndex[0]], this.pickerList[monthIdx][this
							.pickerIndex[
								monthIdx]
						], this.pickerList[dayIdx][this.pickerIndex[dayIdx]], dayIdx);
					}
				}
				const result = [];
				this.pickerType.forEach((item, index) => {
					result.push(
						this.pickerList[index][this.pickerIndex[index]])
				});
				this.pickerRes = result;
				this.$emit('change', result);
			},
			/**
			 * @description 关闭选择器
			 * */
			close() {
				this.pickerShow = false;
				this.$emit('update:show', false);
				this.$emit('close');
			},
			/**
			 * @description 选择完毕
			 */
			confirm() {
				if (this.pickerRes.length == 0) {
					this.pickerRes = this.pickerList.map((item, index) => {
						return item[this.pickerIndex[index]];
					});
				}
				this.$emit('confirm', this.pickerRes);
				this.close();
				// #ifdef VUE2
				this.$emit('input', this.pickerRes)
				// #endif
				// #ifdef VUE3
				this.$emit('update:modelValue', this.pickerRes)
				// #endif
			},
			/**
			 * @description 生成年范围
			 */
			getYearData(y, idx = 0, minTimestamp = this.minDate, maxTimestamp = this.maxDate) {
				const currentYear = y || this.pickerRes[idx] || new Date().getFullYear(); // 获取当前年份
				const years = []; // 用于存储年份的数组

				// 计算最小和最大年份
				const minYear = minTimestamp ? new Date(minTimestamp).getFullYear() : currentYear - 15;
				const maxYear = maxTimestamp ? new Date(maxTimestamp).getFullYear() : currentYear + 15;
				// 生成年份范围，确保不超出最小和最大年份限制
				for (let year = minYear; year <= maxYear; year++) {
					years.push(year);
				}

				// 获取当前年份的下标
				const currentYearIndex = years.indexOf(currentYear);
				if (currentYearIndex == -1) {
					currentYearIndex = 0;
				}
				this.$set(this.pickerIndex, idx, currentYearIndex);
				this.$set(this.pickerList, idx, years);

				// 过滤掉 null 值
				this.pickerList = this.pickerList.filter(item => item !== null);
				this.pickerIndex = this.pickerIndex.filter(item => item !== null);
			},
			/**
			 * @description 生成月范围
			 */
			getMonthData(year, m, idx = 1, minTimestamp = this
				.minDate, maxTimestamp = this.maxDate) {
				const currentYear = year || this.pickerRes[0] || new Date().getFullYear(); // 获取当前年份
				const currentMonth = m || this.pickerRes[idx] || new Date().getMonth() + 1; // 获取当前月份
				const months = []; // 用于存储月份的数组

				// 计算最小和最大月份
				let minMonth = 1; // 默认最小月份为1
				let maxMonth = 12; // 默认最大月份为12

				if (minTimestamp) {
					const minDate = new Date(minTimestamp);
					if (minDate.getFullYear() == currentYear) {
						minMonth = minDate.getMonth() + 1; // 如果最小时间戳的年份等于当前年份，则限制最小月份
					}
				}

				if (maxTimestamp) {
					const maxDate = new Date(maxTimestamp);
					if (maxDate.getFullYear() == currentYear) {
						maxMonth = maxDate.getMonth() + 1; // 如果最大时间戳的年份等于当前年份，则限制最大月份
					}
				}

				// 生成月份范围，确保不超出最小和最大月份限制
				for (let month = minMonth; month <= maxMonth; month++) {
					months.push(month.toString().padStart(2, '0')); // 月份补零
				}

				// 获取当前月份的下标
				let currentMonthIndex = months.indexOf(currentMonth.toString().padStart(2, '0'));
				if (currentMonthIndex === -1) {
					currentMonthIndex = 0; // 如果当前月份不在范围内，则默认选中第一个月份
				}

				// 更新 pickerIndex 和 pickerList
				this.$set(this.pickerIndex, idx, currentMonthIndex);
				this.$set(this.pickerList, idx, months);

				// 过滤掉 null 值
				this.pickerList = this.pickerList.filter(item => item !== null);
				this.pickerIndex = this.pickerIndex.filter(item => item !== null);
			},
			/**
			 * @description 生成日范围
			 */
			getDayData(year, month, d, idx = 2, minTimestamp = this.minDate, maxTimestamp = this.maxDate) {
				const currentYear = year || this.pickerRes[0] || new Date().getFullYear(); // 获取当前年份
				const currentMonth = month || this.pickerRes[1] || new Date().getMonth() + 1; // 获取当前月份
				const currentDay = d || this.pickerRes[idx] || new Date().getDate(); // 获取当前日期
				const days = []; // 用于存储日期的数组


				// 计算当前月份的总天数
				const totalDays = new Date(currentYear, currentMonth, 0).getDate();

				// 计算最小和最大日期
				let minDay = 1; // 默认最小日期为1
				let maxDay = totalDays; // 默认最大日期为当前月份的总天数

				if (minTimestamp) {
					const minDate = new Date(minTimestamp);
					if (minDate.getFullYear() == currentYear && minDate.getMonth() + 1 == currentMonth) {
						minDay = minDate.getDate(); // 如果最小时间戳的年份和月份等于当前年份和月份，则限制最小日期
					}
				}

				if (maxTimestamp) {
					const maxDate = new Date(maxTimestamp);
					if (maxDate.getFullYear() == currentYear && maxDate.getMonth() + 1 == currentMonth) {
						maxDay = maxDate.getDate(); // 如果最大时间戳的年份和月份等于当前年份和月份，则限制最大日期
					}
				}

				// 生成日期范围，确保不超出最小和最大日期限制
				for (let day = minDay; day <= maxDay; day++) {
					days.push(day.toString().padStart(2, '0')); // 日期补零
				}


				// 获取当前日期的下标
				let currentDayIndex = days.indexOf(currentDay.toString().padStart(2, '0'));
				if (currentDayIndex === -1) {
					currentDayIndex = days.length - 1; // 如果当前日期不在范围内，则默认选中最后一个日期
				}


				// 更新 pickerIndex 和 pickerList
				this.$set(this.pickerIndex, idx, currentDayIndex);
				this.$set(this.pickerList, idx, days);

				// 过滤掉 null 值
				this.pickerList = this.pickerList.filter(item => item !== null);
				this.pickerIndex = this.pickerIndex.filter(item => item !== null);
			},
			/**
			 * @description 获取当前小时
			 */
			getCurrentHour(h, idx = 3) {
				const hours = [];
				for (let hour = 0; hour <= 23; hour++) {
					hours.push(hour.toString().padStart(2, '0'))
				}
				h = h || this.pickerRes[idx] || new Date().getHours();
				const currentHourIndex = hours.indexOf(h.toString().padStart(2, '0'));
				if (currentHourIndex == -1) {
					currentHourIndex = 0;
				}
				this.$set(this.pickerIndex, idx, currentHourIndex)
				this.$set(this.pickerList, idx, hours)
				this.pickerList = this.pickerList.filter(item => item !== null);
				this.pickerIndex = this.pickerIndex.filter(item => item !== null);
			},
			/**
			 * @description 获取当前分钟
			 */
			getCurrentMinute(m, idx = 4) {
				const minutes = [];
				for (let minute = 0; minute <= 59; minute++) {
					minutes.push(minute.toString().padStart(2, '0'))
				}
				m = m || this.pickerRes[idx] || new Date().getMinutes();
				const currentMinuteIndex = minutes.indexOf(m.toString().padStart(2, '0'));
				if (currentMinuteIndex == -1) {
					currentMinuteIndex = 0;
				}
				this.$set(this.pickerIndex, idx, currentMinuteIndex)
				this.$set(this.pickerList, idx, minutes)
				this.pickerList = this.pickerList.filter(item => item !== null);
				this.pickerIndex = this.pickerIndex.filter(item => item !== null);
			},
			/**
			 * @description 获取当前秒
			 */
			getCurrentSecond(s, idx = 5) {
				const seconds = [];
				for (let second = 0; second <= 59; second++) {
					seconds.push(second.toString().padStart(2, '0'))
				}
				s = s || this.pickerRes[idx] || new Date().getSeconds();
				const currentSecondIndex = seconds.indexOf(s.toString().padStart(2, '0'));
				if (currentSecondIndex == -1) {
					currentSecondIndex = 0;
				}
				this.$set(this.pickerIndex, idx, currentSecondIndex)
				this.$set(this.pickerList, idx, seconds)
				this.pickerList = this.pickerList.filter(item => item !== null);
				this.pickerIndex = this.pickerIndex.filter(item => item !== null);
			}
		},
	};
</script>
<style scoped>
	@import "./style.css";
</style>